import _ from 'lodash';
import logger from '../../../common/logger';

import { AgentDto } from '../../model/agent/dtos';
import { SimJobStatus } from '../../model/simulation/type';
import { SimulationJobService } from '../../model/simulation/service';
import { ISimulationJobService } from '../../model/simulation/service';
export interface AgentSelectionStrategy {
  select(agents: AgentDto[]): Promise<AgentDto>;
}

export class RandomSelectionStrategy implements AgentSelectionStrategy {
  async select(agents: AgentDto[]): Promise<AgentDto> {

    logger.info('randowm select begin....');
    const jobService = new SimulationJobService();

    const runningJobs = await jobService.findByStatus(SimJobStatus.Running);

    if (_.isEmpty(runningJobs)) {
      return _.sample(agents);
    }

    const runningAgentIds = runningJobs.map(j => j.agentId);

    logger.info(`runningAgentIds: ${runningAgentIds.join(',')}`);

    const fressAgents = agents.filter(a => {
      return !_.includes(runningAgentIds, a.identity);
    });

    logger.info(`fressAgents: ${fressAgents}`);

    if (_.isEmpty(fressAgents)) {
      return _.sample(agents);
    }

    return _.sample(fressAgents);
  }
}
export class SingletonSelectonStrategy implements AgentSelectionStrategy {

  constructor(private productName: string, private jobService: ISimulationJobService) {}

  async select(agents: AgentDto[]): Promise<AgentDto> {
    const runningJobs = await this.jobService.findByProductAndStatus(this.productName, SimJobStatus.Running);
    if (runningJobs.length === 0) {
      return _.sample(agents);
    }
  }
}
export class MinRunningAndQueuingStrategy implements AgentSelectionStrategy {

  constructor(private productName: string) {}

  async select(agents: AgentDto[]): Promise<AgentDto> {
    if (agents.length === 1) {
      return agents[0];
    }
    const jobService = new SimulationJobService();
    const agentIds = agents.map(j => j.identity);
    const runningAndQueuingStatuses = [SimJobStatus.Created, SimJobStatus.AgentAssigned, SimJobStatus.AgentDispatched, SimJobStatus.Running];
    const agentJobCounts = await jobService.countJobByAgentAndStatuses(agentIds, runningAndQueuingStatuses, this.productName);
    logger.info(`select agent, agentJobCounts = ${JSON.stringify(agentJobCounts)}`);
    if (!_.isEmpty(agentJobCounts)) {
      // agentJobCounts 统计的是有排队和运行中的agent,需要先筛选没有任务的agent
      const queuingAgentIds = agentJobCounts.map(j => j.identity);
      const freeAgents = agents.filter(agent => !_.includes(queuingAgentIds, agent.identity));
      if (!_.isEmpty(freeAgents)) {
        logger.info(`select agent, freeAgents = ${JSON.stringify(freeAgents)}`);
        return getOptimalResourceAgent(freeAgents);
      }
      const sortJobs = _.sortBy(agentJobCounts, ['count'], 'asc');
      const freeAgent = agents.filter(agent => agent.identity === sortJobs[0].identity);
      return _.sample(freeAgent);
    }
    return _.sample(agents);
  }
}

function getOptimalResourceAgent(agents: AgentDto[]) {
  if (_.isEmpty(agents) || agents.length < 1) {
    logger.warn('get optimal resource agent failed, agents is empty');
    return;
  }
  const cpuFreeAgents = agents.filter(agent => !_.isEmpty(agent.cpu) && agent.cpu.usage < 70);
  const memoryFreeAgents = agents.filter(agent => !_.isEmpty(agent.memory) && agent.memory.usedMemPercentage < 70);
  const freeAgents = _.intersectionWith(cpuFreeAgents, memoryFreeAgents, (src, des) => {
    return src.identity === des.identity;
  });
  if (freeAgents.length > 0) {
    return _.sample(freeAgents);
  }
  if (cpuFreeAgents.length > 0) {
    return _.sample(cpuFreeAgents);
  }
  if (memoryFreeAgents.length > 0) {
    return _.sample(memoryFreeAgents);
  }
  return _.sample(agents);
}

export class AgentSelectionStrategyFactory {
  static newSelectionStrategy(productName: string): AgentSelectionStrategy {
    // if (productName === 'axisuite') {
    //   const jobService = new SimulationJobService();
    //   return new SingletonSelectonStrategy(productName, jobService);
    // }
    // return new RandomSelectionStrategy();
    return new MinRunningAndQueuingStrategy(productName);
  }
}